 
/* DOM abbreviation function */
function newNode(tagname){
 
  var node = document.createElement(tagname);
 
  for( var i=1;i<arguments.length;i++ ){
 
    if(typeof arguments[i] == 'string'){ //Text
      node.appendChild( document.createTextNode(arguments[i]) );
 
    }else if(typeof arguments[i] == 'object' && arguments[i] != null){ 
 
      if(arguments[i].nodeName){ //If it is a DOM Node
        node.appendChild(arguments[i]);
 
      }else{ //Attributes (hopefully)
        for(var j in arguments[i]){
          if(j == 'class'){ //Classname different because...
            node.className = arguments[i][j];
 
          }else if(j == 'style'){ //Style is special
            node.style.cssText = arguments[i][j];
 
          }else if(typeof arguments[i][j] == 'function'){ //Basic event handlers
            try{ node.addEventListener(j,arguments[i][j],false); //W3C
            }catch(e){try{ node.attachEvent('on'+j,arguments[i][j],"Language"); //MSIE
            }catch(e){ node['on'+j]=arguments[i][j]; }}; //Legacy
 
          }else if(arguments[i][j]){
            node.setAttribute(j,arguments[i][j]); //Normal attributes
 
          }
        }
      }
    }
  }
 
  return node;
}

function getNode(id)
{
	return document.getElementById(id);
}
function inspect(obj, level)
{

	level = level || 10;
  
	stack = []
	function recurse(obj)
	{
		try{
			if (typeof(obj) == 'object')
			{
				if (obj == null)
					return newNode('b','null');
				for (var i=0; i<stack.length; i++)
				{
					if (stack[i] == obj)
						return newNode('b', {style: 'color: #F00'}, 'LOOP!!');
				}
				if (stack.len > level)
					return newNode('i', '...');
				stack.push(obj);
				if (obj instanceof Array)
				{
					var out;
					if (obj.length == 0)
						out = newNode('b', "[ ]");
					else
					{
						out = newNode('ol', {start: 0});
						for (var i=0; i<obj.length; i++)
						 	out.appendChild(newNode('li',recurse(obj[i])));	
					}
					stack.pop();
					return out;
				}
				var out = newNode('ul');
				for (var i in obj) 
				{
					out.appendChild(newNode('li', recurse(i), document.createTextNode(' : '), recurse(obj[i])));
				}
				stack.pop();
				return out;
			}
			if (typeof(obj) == 'boolean')
				return newNode('b', obj+"");

			if (typeof(obj) == 'number')
				return newNode('b', obj+"");
			
			return newNode('span',{style:'border-right: 1px solid #AAA; border-left: 1px solid #AAA; white-space: pre;'}, String(obj));	
		} catch (e) {
			return newNode('i', '???');
		}
				
	}
	
	return newNode('div', {style: 'font-family: Monospace'},
		recurse(obj)
	);	
}

function htmlEscape(str)
{
	return str.replace("&", "&amp;").replace("<", "&lt;").replace("\"","&quot;").replace(" ","&nbsp;").replace(/(\r\n|\r(!=\n))/g,"\n");
}

/**
 * A group of functions for serializing HTML in a way that is the same in all browsers.
 * The TinyMCE functions are not stable enough cross-browser to allow them to be used 
 * in diffs. 
 *
 * These innerHTML and outerHTML functions work in all browsers that tinyMCE does, and,
 * more importantly return the same string for the same DOM fragment in them all.
 *
 * There are a few edge cases where the return value is not perfect, however, as far as
 * I've been able to test, it always stabalises within one "get/set" cycle. i.e. by using
 * function getHTML(e) 
 * {
 *     e.innerHTML = this.innerHTML(e);
 *     return this.innerHTML(e);
 * }
 *
 * There is one MAJOR issue with this function. It can cause changes to the DOM. SHIT.
 */
EditableDom = {
	_nodeName: function (node)
	{
		var nn = node.nodeName.toLowerCase();

		if (nn == "strong") nn = "b";
		if (nn == "em") nn = "i";
		if (nn.charAt(0) == "/") throw "HATE IE";
		return nn;

	},
	outerHTML: function (node)
	{
		if (node.nodeType == 3)
			return htmlEscape(node.nodeValue);

		var nona = this._nodeName(node);
		var output = "<" + nona 
		var atts = [];
		var mce_style = 0;
		for (var i=0; i<node.attributes.length; i++)
		{
			var att = node.attributes[i];
			//This is to get round IE.
			// The first few are fine, "inherit" "false" and "0" just get round some bizarre special cases. Maybe worth coding them more explicitly
			if (att.value != null && att.value != "" && att.value != "null" && att.value != "inherit" && att.value != "false" && att.value != "0" )
			{
				atts.push(att.name.toLowerCase()+"=\""+ htmlEscape(att.value) + "\"")

				if(att.name.toLowerCase() == "style")
				{
					mce_style -= 1;
					//This is to get around firefox - all the other's agree that lowercase hex is the way to go.
					atts[atts.length-1] = atts[atts.length-1].replace(/rgb *\( *([0-9]*) *, *([0-9]*) *, *([0-9]*) *\)/, function (match, r, g, b) {
						function hex(d) {
							if (d.toString(16).length == 1)
								return '0' + d.toString(16);
							else
								return d.toString(16);
						}
						return "#"+ hex(Number(r))+hex(Number(g))+hex(Number(b));
					});
				}
				if(att.name.toLowerCase() == "mce_style")
					mce_style += 1;
			}
		}
		//Guess which browser needs this???
		if (mce_style > 0)
		{
			atts.push("style=\""+node.getAttribute("mce_style")+"\"");
		}
		if (atts.length > 0)
		{
			atts = atts.sort();
			output += " "+atts.join(" ");
		}

		var str = this.innerHTML (node)

		if (str != "")
		{
			return output + ">" + str + "</"+nona+">";
		}
		else
		{
			return output + "/>";
		}
	},
	innerHTML: function (node)
	{
		if(node.nodeType == 3)
			return node.nodeValue;
		
		var	output = "";
		for(var i=0; i<node.childNodes.length; i++)
		{
			try
			{
				output += this.outerHTML(node.childNodes[i]);
			}
			catch (e)
			{
				if (e != "HATE IE") throw e;
				//this takes the biscuit. Occasionally; for no good reason; IE will replace a node with two nodes,
				// which without this hack come out as "<abbr style=""... />....</abbr/>" (yes .nodeName == "/abbr")
				var nn; var j=i-1;
				do {
					var nn = this._nodeName(node.childNodes[j]);
				} while (j-- && nn == "#text");
				output = output.replace(/\/>([^>]*)$/,">$1") + "</"+nn+">";

			}
		}
		//Say bye to all spaces - IE already deleted them :(
		output = output.replace(/^\s+/,"").replace(/^\s+$/,"").replace(/>[\s\n\r]+/g,">").replace(/[\s\n\r]+</g,"<");
		return output;
	}
}
